package com.itaming.lycheeframework.support.utils;

import lombok.experimental.UtilityClass;

import java.util.function.IntPredicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 脱敏工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class DesensitizationUtil {

    /**
     * 默认掩码字符
     */
    private static final char DEFAULT_MASK = '*';

    /**
     * 地址脱敏
     * <p>
     * 保留前3位，其他字符进行脱敏，如：北京市******，掩码统一6位，
     * 小于4位则不脱敏，直接返回原字符串
     *
     * @param address 地址
     * @return 脱敏后的地址
     */
    public static String address(String address) {
        return address(address, DEFAULT_MASK);
    }

    /**
     * 地址脱敏
     * <p>
     * 保留前3位，其他字符进行脱敏，如：北京市******，掩码统一6位，
     * 小于4位则不脱敏，直接返回原字符串
     *
     * @param address 地址
     * @param mask    掩码字符
     * @return 脱敏后的地址
     */
    public static String address(String address, char mask) {
        address = StringUtil.deleteWhitespace(address);
        return StringUtil.length(address) < 4 ? address : address.substring(0, 4) + StringUtil.repeat(mask, 6);
    }

    /**
     * 银行卡号脱敏
     * <p>
     * 保留前4位和后2位，其他字符进行脱敏，如：6271**********99，
     * 小于8位则不脱敏，直接返回原字符串
     *
     * @param bankCard 银行卡号
     * @return 脱敏后的银行卡号
     */
    public static String bankCard(String bankCard) {
        return bankCard(bankCard, DEFAULT_MASK);
    }

    /**
     * 银行卡号脱敏
     * <p>
     * 保留前4位和后2位，其他字符进行脱敏，如：6271**********99，
     * 小于8位则不脱敏，直接返回原字符串
     *
     * @param bankCard 银行卡号
     * @param mask     掩码字符
     * @return 脱敏后的银行卡号
     */
    public static String bankCard(String bankCard, char mask) {
        bankCard = StringUtil.deleteWhitespace(bankCard);
        return StringUtil.length(bankCard) < 8 ? bankCard : desensitize(bankCard, 4, bankCard.length() - 2, mask);
    }

    /**
     * 车牌号脱敏
     * <p>
     * 保留前2位和后1位，其他字符进行脱敏，如：京A****9
     *
     * @param carLicense 车牌号
     * @return 脱敏后的车牌号
     */
    public static String carLicense(String carLicense) {
        return carLicense(carLicense, DEFAULT_MASK);
    }

    /**
     * 车牌号脱敏
     * <p>
     * 保留前2位和后1位，其他字符进行脱敏，如：京A****9
     *
     * @param carLicense 车牌号
     * @param mask       掩码字符
     * @return 脱敏后的车牌号
     */
    public static String carLicense(String carLicense, char mask) {
        carLicense = StringUtil.deleteWhitespace(carLicense);
        return desensitize(carLicense, 2, carLicense.length() - 1, mask);
    }

    /**
     * 姓名脱敏
     * <p>
     * 保留第一个字符，其他字符进行脱敏，如：张*，李**，J***
     *
     * @param name 姓名
     * @return 脱敏后的姓名
     */
    public static String name(String name) {
        return name(name, DEFAULT_MASK);
    }

    /**
     * 姓名脱敏
     * <p>
     * 保留第一个字符，其他字符进行脱敏，如：张*，李**，J***
     *
     * @param name 姓名
     * @return 脱敏后的姓名
     */
    public static String name(String name, char mask) {
        int length = StringUtil.length(name);
        return length < 2 ? name : desensitize(name, 1, length, mask);
    }

    /**
     * 大陆身份证号脱敏
     * <p>
     * 保留前4位和后2位，其他字符进行脱敏，如：110108**********1X
     *
     * @param id 身份证号
     * @return 脱敏后的身份证号
     */
    public static String chineseId(String id) {
        return chineseId(id, DEFAULT_MASK);
    }

    /**
     * 大陆身份证号脱敏
     * <p>
     * 保留前4位和后2位，其他字符进行脱敏，如：110108**********1X
     *
     * @param id 身份证号
     * @return 脱敏后的身份证号
     */
    public static String chineseId(String id, char mask) {
        return StringUtil.isEmpty(id) ? id : desensitize(id, 4, id.length() - 2, mask);
    }

    /**
     * 大陆手机号脱敏
     * <p>
     * 保留前3位和后4位，其他字符进行脱敏，如：138****1234
     *
     * @param value 待脱敏字符串
     * @return 脱敏后的字符串
     */
    public static String chineseMobile(String value) {
        return chineseMobile(value, DEFAULT_MASK);
    }

    /**
     * 大陆手机号脱敏
     * <p>
     * 保留前3位和后4位，其他字符进行脱敏，如：138****1234
     *
     * @param value 待脱敏字符串
     * @return 脱敏后的字符串
     */
    public static String chineseMobile(String value, char mask) {
        return StringUtil.isEmpty(value) ? value : value.replaceAll("(1\\d{2})\\d{4}(\\d{4})", "$1" + StringUtil.repeat(mask, 4) + "$2");
    }

    /**
     * 固定电话脱敏
     * <p>
     * 电话号码保留后四位，其他字符进行脱敏，如果有区号和横线，区号保留前2位，如：****8888，01*-****8888
     *
     * @param value 待脱敏字符串
     * @return 脱敏后的字符串
     */
    public static String telephone(String value) {
        return telephone(value, DEFAULT_MASK);
    }

    /**
     * 固定电话脱敏
     * <p>
     * 电话号码保留后四位，其他字符进行脱敏，如果有区号和横线，区号保留前2位，如：****8888，01*-****8888
     *
     * @param value 待脱敏字符串
     * @return 脱敏后的字符串
     */
    public static String telephone(String value, char mask) {
        if (StringUtil.isEmpty(value)) {
            return value;
        }

        Matcher matcher = Pattern.compile("(\\d+-)?(\\d+)").matcher(value);
        if (!matcher.matches()) {
            return value;
        }

        String g1 = matcher.group(1);
        String g2 = matcher.group(2);

        String matched = desensitize(g2, 0, g2.length() < 6 ? g2.length() - 2 : g2.length() - 4, mask);
        return g1 == null ? matched : desensitize(g1, 2, g1.length() - 1) + matched;
    }

    /**
     * 邮箱脱敏
     * <p>
     * 保留@符号前的第1个字符，其他字符进行脱敏，如：a******@example.com，
     * 没有@符号则不脱敏，直接返回原字符串
     *
     * @param value 待脱敏字符串
     * @return 脱敏后的字符串
     */
    public static String email(String value) {
        return email(value, DEFAULT_MASK);
    }

    /**
     * 邮箱脱敏
     * <p>
     * 保留@符号前的第1个字符，其他字符进行脱敏，如：a******@example.com，
     * 没有@符号则不脱敏，直接返回原字符串
     *
     * @param email 邮箱
     * @return 脱敏后的邮箱
     */
    public static String email(String email, char mask) {
        int atIndex = email.indexOf('@');
        return atIndex < 1 ? email : desensitize(email, atIndex == 1 ? 0 : 1, atIndex, mask);
    }

    /**
     * IPv4地址脱敏
     * <p>
     * 保留第1段地址，其他进行脱敏，如：127.*.*.*
     *
     * @param ipv4 IPv4 地址
     * @return 脱敏后的 IPv4 地址
     */
    public static String ipv4(String ipv4) {
        return ipv4(ipv4, DEFAULT_MASK);
    }

    /**
     * IPv4地址脱敏
     * <p>
     * 保留第1段地址，其他进行脱敏，如：127.*.*.*
     *
     * @param ipv4 IPv4 地址
     * @param mask 掩码字符
     * @return 脱敏后的 IPv4 地址
     */
    public static String ipv4(String ipv4, char mask) {
        return StringUtil.isEmpty(ipv4) ? ipv4 : StringUtil.substringBefore(ipv4, StringPool.DOT) + StringUtil.repeat(StringPool.DOT + mask, 3);
    }

    /**
     * IPv6地址脱敏
     * <p>
     * 保留第1段地址，其他进行脱敏，如：fe80:*:*:*:*:*:*:*
     *
     * @param ipv6 IPv6 地址
     * @return 脱敏后的 IPv6 地址
     */
    public static String ipv6(String ipv6) {
        return ipv6(ipv6, DEFAULT_MASK);
    }

    /**
     * IPv6地址脱敏
     * <p>
     * 保留第1段地址，其他进行脱敏，如：fe80:*:*:*:*:*:*:*
     *
     * @param ipv6 IPv6 地址
     * @param mask 掩码字符
     * @return 脱敏后的 IPv6 地址
     */
    public static String ipv6(String ipv6, char mask) {
        return StringUtil.isEmpty(ipv6) ? ipv6 : StringUtil.substringBefore(ipv6, StringPool.COLON) + StringUtil.repeat(StringPool.COLON + mask, 7);
    }

    /**
     * 密码脱敏
     * <p>
     * 全部进行脱敏，总长度6位
     *
     * @param password 密码
     * @return 脱敏后的密码
     */
    public static String password(String password) {
        return password(password, DEFAULT_MASK);
    }

    /**
     * 密码脱敏
     * <p>
     * 全部进行脱敏，总长度统一6位
     *
     * @param password 密码
     * @param mask     掩码字符
     * @return 脱敏后的密码
     */
    public static String password(String password, char mask) {
        return StringUtil.isEmpty(password) ? password : StringUtil.repeat(mask, 6);
    }

    /**
     * 脱敏
     *
     * @param value      待脱敏字符串
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @return 脱敏后的字符串
     */
    public static String desensitize(String value, int startIndex, int endIndex) {
        return desensitize(value, startIndex, endIndex, DEFAULT_MASK);
    }

    /**
     * 脱敏
     *
     * @param value      待脱敏字符串
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @param mask       掩码字符
     * @return 脱敏后的字符串
     */
    public static String desensitize(String value, int startIndex, int endIndex, char mask) {
        if (StringUtil.isEmpty(value) || startIndex > value.length() || endIndex <= 0) {
            return value;
        }

        if (startIndex < 0) {
            startIndex = 0;
        }

        if (endIndex > value.length()) {
            endIndex = value.length();
        }

        if (startIndex >= endIndex) {
            return value;
        }

        return new StringBuilder(value).replace(startIndex, endIndex, StringUtil.repeat(mask, endIndex - startIndex)).toString();
    }

    /**
     * 脱敏
     *
     * @param value          待脱敏字符串
     * @param indexPredicate 索引断言
     * @return 脱敏后的字符串
     */
    public static String desensitize(String value, IntPredicate indexPredicate) {
        return desensitize(value, indexPredicate, DEFAULT_MASK);
    }

    /**
     * 脱敏
     *
     * @param value          待脱敏字符串
     * @param indexPredicate 索引断言
     * @param mask           掩码字符
     * @return 脱敏后的字符串
     */
    public static String desensitize(String value, IntPredicate indexPredicate, char mask) {
        if (StringUtil.isEmpty(value) || indexPredicate == null) {
            return value;
        }

        int length = StringUtil.length(value);
        StringBuilder sb = new StringBuilder(value);
        for (int i = 0; i < length; i++) {
            if (indexPredicate.test(i)) {
                sb.setCharAt(i, mask);
            }
        }

        return sb.toString();
    }

}
